﻿using System;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace CCPSIPCallPush
{
    public partial class Service1 : ServiceBase
    {
        private const int kServicePort = 9530;
        private static TcpListener listener;
        private static Thread listenerThread;
        private const int kMaxClientQueue = 30000;
        private const int kMaxReadKB = 100;


        private static bool pleaseWork = true;
        private static Thread workThread;


        public Service1()
        {
            InitializeComponent();
        }


        #region Service Start/Stop

        protected override void OnStart(string[] args)
        {
            Kit.ChrisKit.AppendToDayLog("Push Service Start...");
            Kit.ChrisKit.AppendToDayLog("Service Version: 2014/09/16 10:30");

            listenerThread = new Thread(StartListen);
            listenerThread.Start();

            Kit.ApnsKit.OpenConnection();
            workThread = new Thread(StartWork);
            workThread.Start();
        }


        protected override void OnStop()
        {
            listenerThread.Abort();
            listenerThread = null;
            listener.Stop();
            listener = null;

            workThread.Abort();
            workThread = null;
            Kit.ApnsKit.CloseConnection();
            Kit.ChrisKit.AppendToDayLog("Push Service Stop.");
        }

        #endregion


        private void StartListen()
        {
            try
            {
                // Start Service Listener
                //
                listener = new TcpListener(IPAddress.Any, kServicePort);
                listener.Start(kMaxClientQueue);
                Kit.ChrisKit.AppendToDayLog(string.Format("Start Listening on Port {0}...", kServicePort));

                int clientCount = 0;

                // Enter the listening loop
                //
                while (true)
                {
                    // Perform a blocking call to accept requests
                    TcpClient clientSocket = listener.AcceptTcpClient();
                    clientCount++;
                    if (clientCount > 1000000)
                    {
                        clientCount = 1;
                    }
                    new ClientThread(clientCount, clientSocket).Start();
                }
            }
            catch (ThreadAbortException ex)
            {
                Kit.ChrisKit.SkipVar(ex);
            }
            catch (Exception ex)
            {
                Kit.ChrisKit.AppendToDayLog(Kit.ChrisKit.Ex2Xml(ex, true));
            }
        }


        private void StartWork()
        {
            while (true)
            {
                if (pleaseWork)
                {
                    pleaseWork = false;

                    //檢查待推播訊息並傳送
                    CallPushWoker.DoWork();
                }
                else
                {
                    Thread.Sleep(100);
                }
            }
        }


        /// <summary>
        /// Class to handle each client request
        /// </summary>
        private class ClientThread
        {
            int ClientID;
            TcpClient ClientSocket;

            public ClientThread(int clientID, TcpClient clientSocket)
            {
                this.ClientID = clientID;
                this.ClientSocket = clientSocket;
            }

            public void Start()
            {
                Thread newThread = new Thread(doService);
                newThread.IsBackground = true;
                newThread.Start();
            }

            private void doService()
            {
                byte[] receivedData = new byte[kMaxReadKB * 1024];
                byte[] buffer = new byte[4096];
                string clientIP = ((IPEndPoint)ClientSocket.Client.RemoteEndPoint).Address.ToString();

                try
                {
                    NetworkStream netStream = ClientSocket.GetStream();

                    int readDataTimes = 0;
                    int readNullTimes = 0;

                    while (ClientSocket.Connected)
                    {
                        //【01】讀取Request
                        //
                        int totalRead = 0;
                        int readCount = 0;

                        try
                        {
                            readCount = netStream.Read(buffer, 0, buffer.Length);
                        }
                        catch (System.IO.IOException ex)
                        {
                            if (ex.InnerException is SocketException)
                            {
                                SocketException sEx = (SocketException)ex.InnerException;
                                if (sEx.SocketErrorCode == SocketError.ConnectionAborted)
                                    break;
                            }
                            throw ex;
                        }

                        if (readCount > 0)
                        {
                            Buffer.BlockCopy(buffer, 0, receivedData, totalRead, readCount);
                            totalRead += readCount;
                        }

                        //【01-1】檢查是否還有連續資料
                        //
                        while (netStream.DataAvailable)
                        {
                            readCount = netStream.Read(buffer, 0, buffer.Length);
                            if (readCount > 0)
                            {
                                if (totalRead + readCount <= receivedData.Length)
                                {
                                    Buffer.BlockCopy(buffer, 0, receivedData, totalRead, readCount);
                                    totalRead += readCount;
                                }
                                else
                                {
                                    int finalCount = receivedData.Length - totalRead;
                                    Buffer.BlockCopy(buffer, 0, receivedData, totalRead, finalCount);
                                    totalRead += finalCount;
                                }
                            }
                        }

                        //【02】處理指令
                        //
                        if (totalRead > 0)
                        {
                            bool isValidCmd = false;

                            string dataStr = Encoding.UTF8.GetString(receivedData, 0, totalRead);
                            if (dataStr.ToUpper() == "PUSH")
                            {
                                isValidCmd = true;
                                pleaseWork = true;
                            }

                            string logStr = string.Format("({0}) {1}\t{2}", ClientID, clientIP, dataStr);


                            //【03】回傳Response
                            //
                            if (netStream.CanWrite)
                            {
                                string respStr = (isValidCmd ? "OK" : "BAN");
                                byte[] respBytes = Encoding.UTF8.GetBytes(respStr);
                                netStream.Write(respBytes, 0, respBytes.Length);
                                netStream.Flush();

                                logStr += string.Format("\t{0}", respStr);
                            }
                            //Kit.ChrisKit.AppendToDayLog(logStr);


                            //【04-1】傳輸次數太多則停止連線
                            //
                            readDataTimes++;
                            if (readDataTimes > 10000)
                                break;
                        }
                        else
                        {
                            //【04-2】讀不到資料次數太多視為斷線
                            //
                            readNullTimes++;
                            if (readNullTimes > 100)
                                break;
                        }
                    }// End of while


                    //【99】Clean Up
                    //
                    netStream.Close();
                    ClientSocket.Close();
                }
                catch (Exception ex)
                {
                    Kit.ChrisKit.AppendToDayLog(Kit.ChrisKit.Ex2Xml(ex, true));
                }
            }

        }//End of class ClientThread

        //for debug
        public void Start(string[] args)
        {
            this.OnStart(args);
        }

        //for debug
        public void Stop()
        {
            this.OnStop();
        }
    }
}
